﻿using Newtonsoft.Json;
using SubWayElevatorPower.BUS;
using SubWayElevatorPower.Models;
using SubWayElevatorPower.Socket;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using System.Net.WebSockets;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace SubWayElevatorPower.Serial
{
    public class SerialDataProcess
    {
        public static List<string> cmdlist = new List<string>();
        public static List<ElevatorDetail> ele_detail = new List<ElevatorDetail>();

        #region 485_1
        public static SerialPort my485SerialPort_1 = new SerialPort();
        private System.Timers.Timer SerialPortRecvTimer485_1;
        private static int pSerialPortRecv_485_1 = 0;
        private static int byteLength = 40960;
        public byte[] byteSerialPortRecv485_1 = new byte[byteLength];
        public event EventHandler<SerialPort485_1RecvEventArgs> ReceivedDataEvent485_1;


        public class SerialPort485_1RecvEventArgs : EventArgs
        {
            /// <summary>
            /// 广播 收到的串口数据
            /// </summary>
            public readonly byte[] RecvData = new byte[byteLength];

            /// <summary>
            /// 收到数据 的 长度
            /// </summary>
            public readonly int RecvDataLength;

            /// <summary>
            /// 将 收到的数据 转化成 待广播的数据
            /// </summary>
            public SerialPort485_1RecvEventArgs(byte[] recvData, int recvDataLength)
            {
                recvData.CopyTo(RecvData, 0);
                RecvDataLength = recvDataLength;
            }
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <param name="portName">串口号</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="parity">校验位</param>
        /// <param name="dataBits">数据位</param>
        /// <param name="stopBits">停止位</param>
        public void Open485SerialPort_1(string portName, int baudRate, int parity, int dataBits, int stopBits)
        {
            try
            {
                Set485SerialPortConfig(portName, baudRate, parity, dataBits, stopBits);
                my485SerialPort_1.Open();
            }
            catch (Exception ex)
            {
                LogHelper.Log("打开串口失败"+ex.ToString());
            }
        }

        private void Set485SerialPortConfig(string portName, int baudRate, int parity, int dataBits, int stopBits)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                portName = $"/dev/{portName}";
            }
            // 串口 参数设置
            my485SerialPort_1.PortName = portName;
            my485SerialPort_1.BaudRate = baudRate;
            switch (parity)
            {
                case 0:
                default:
                    my485SerialPort_1.Parity = Parity.None;
                    break;
                case 1:
                    my485SerialPort_1.Parity = Parity.Odd;
                    break;
                case 2:
                    my485SerialPort_1.Parity = Parity.Even;
                    break;
                case 3:
                    my485SerialPort_1.Parity = Parity.Mark;
                    break;
                case 4:
                    my485SerialPort_1.Parity = Parity.Space;
                    break;
            }
            my485SerialPort_1.DataBits = ((4 < dataBits) && (dataBits < 9)) ? dataBits : 8;
            switch (stopBits)
            {
                case 0:
                    my485SerialPort_1.StopBits = StopBits.None;
                    break;
                case 1:
                default:
                    my485SerialPort_1.StopBits = StopBits.One;
                    break;
                case 2:
                    my485SerialPort_1.StopBits = StopBits.OnePointFive;
                    break;
                case 3:
                    my485SerialPort_1.StopBits = StopBits.Two;
                    break;
            }
            my485SerialPort_1.RtsEnable = true;
            my485SerialPort_1.DataReceived += new SerialDataReceivedEventHandler(Data485Received_1);

            // 串口 接收数据处理定时器 参数设置
            SerialPortRecvTimer485_1 = new System.Timers.Timer();
            SerialPortRecvTimer485_1.Interval = 100;
            SerialPortRecvTimer485_1.AutoReset = false;
            SerialPortRecvTimer485_1.Elapsed += new ElapsedEventHandler(SP485RecvTimer_Tick_1);
        }

        private void SP485RecvTimer_Tick_1(object sender, EventArgs e)
        {
            byte[] TemporaryData = new byte[byteLength];
            int TemporaryDataLength = 0;

            if (ReceivedDataEvent485_1 != null)
            {
                byteSerialPortRecv485_1.CopyTo(TemporaryData, 0);
                TemporaryDataLength = pSerialPortRecv_485_1;

                ReceivedDataEvent485_1.Invoke(this, new SerialPort485_1RecvEventArgs(TemporaryData, TemporaryDataLength));
                // 数据处理完后，将指针指向数据头，等待接收新的数据
                pSerialPortRecv_485_1 = 0;
            }
        }

        private async void Data485Received_1(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                SerialPortRecvTimer485_1.Stop();
                try
                {
                    byte[] ReadBuf = new byte[my485SerialPort_1.BytesToRead];
                    if (pSerialPortRecv_485_1 > byteLength - ReadBuf.Length)
                    {
                        pSerialPortRecv_485_1 = 0;
                    }
                    my485SerialPort_1.Read(ReadBuf, 0, ReadBuf.Length);
                    ReadBuf.CopyTo(byteSerialPortRecv485_1, pSerialPortRecv_485_1);
                    pSerialPortRecv_485_1 += ReadBuf.Length;

                    for (int i = 0; i < byteSerialPortRecv485_1.Length; i++)
                    {
                        if (byteSerialPortRecv485_1[i + 1] == 0x03 && byteSerialPortRecv485_1[i + 2] == 0x02)
                        {
                            byte[] buffers = new byte[byteSerialPortRecv485_1[i + 2] + 3];
                            for (int j = 0; j < buffers.Length; j++)
                            {
                                buffers[j] = byteSerialPortRecv485_1[i + j];
                            }
                            byte[] crc = Crc18(buffers);
                            if (crc[0] == byteSerialPortRecv485_1[i + 5] && crc[1] == byteSerialPortRecv485_1[i + 6])
                            {
                                Array.Clear(byteSerialPortRecv485_1, i, 7);
                                if (buffers[0] == 0xFF)
                                {
                                    if (ele_detail.Count == 1)
                                    {
                                        byte[] data = new byte[2];
                                        data[0] = buffers[4];
                                        data[1] = buffers[3];
                                        ele_detail[0].deviceid = GetValue(data);
                                        ele_detail[0].cmd = GetCmd(ele_detail[0].deviceid);
                                        PushData();
                                    }
                                }
                                else
                                {
                                    int deviceid = buffers[0];
                                    int index = ele_detail.FindIndex(x => x.deviceid == deviceid);
                                    if (index > -1)
                                    {
                                        byte[] data = new byte[2];
                                        data[0] = buffers[4];
                                        data[1] = buffers[3];
                                        int wd = GetValue(data);
                                        ElevatorDetail elevatorDetail = ele_detail[index];
                                        elevatorDetail.receiveout = 0;
                                        float wd_value = (float)(wd * 0.1 - 50);
                                        elevatorDetail.temperature = wd_value;
                                        elevatorDetail.updateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                        if (wd_value > elevatorDetail.temperaturethreshold)
                                        {
                                            elevatorDetail.alarmTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                            elevatorDetail.alarm_temperature = wd_value;
                                            if (elevatorDetail.type != 2 && elevatorDetail.alarm_flag == 0)
                                            {
                                                elevatorDetail.alarm_flag = 1;
                                            }
                                            elevatorDetail.type = 2;
                                        }
                                        else
                                        {
                                            elevatorDetail.type = 1;
                                        }
                                        ele_detail[index] = elevatorDetail;
                                        PushData();
                                    }

                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {

                }
                SerialPortRecvTimer485_1.Start();
            }
            catch (Exception ex)
            {
            }
        }

        public int GetValue(byte[] data)
        {
            int value = 0;
            try
            {
                BitArray bits;
                bits = new BitArray(data);

                byte[] wd_value = new byte[4];
                wd_value[0] = data[0];
                wd_value[1] = data[1];
                wd_value[2] = 0x00;
                wd_value[3] = 0x00;
                value = BitConverter.ToInt32(wd_value, 0);
            }
            catch (Exception ex)
            {

            }
            return value;
        }

        public byte[] Crc18(byte[] buffer, int start = 0, int len = 0)
        {
            if (buffer == null || buffer.Length == 0) return null;
            if (start < 0) return null;
            if (len == 0) len = buffer.Length - start;
            int length = start + len;
            if (length > buffer.Length) return null;
            ushort crc = 0xFFFF;// Initial value
            for (int i = start; i < length; i++)
            {
                crc ^= buffer[i];
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 1) > 0)
                        crc = (ushort)((crc >> 1) ^ 0xA001);// 0xA001 = reverse 0x8005
                    else
                        crc = (ushort)(crc >> 1);
                }
            }
            byte[] ret = BitConverter.GetBytes(crc);
            //Array.Reverse(ret);
            return ret;
        }

        #endregion

        #region 温度

        public void CmdSend()
        {
            Task t = Task.Factory.StartNew(() =>
            {
                if (ele_detail.Count == 1)
                {
                    SendListMsg("FF 03 00 0F 00 01 A1 D7");
                    Wait(200);
                }
                while (true)
                {
                    try
                    {
                        for (int i = 0; i < ele_detail.Count; i++)
                        {
                            ElevatorDetail elevatorDetail = ele_detail[i];
                            SendListMsg(elevatorDetail.cmd);
                            if (elevatorDetail.type != 0)
                            {
                                elevatorDetail.receiveout++;
                                if (elevatorDetail.receiveout == 4)
                                {
                                    elevatorDetail.type = 0;
                                    elevatorDetail.receiveout = 0;
                                }
                                int index = ele_detail.FindIndex(x => x.deviceid == elevatorDetail.deviceid);
                                if (index > -1)
                                {
                                    ele_detail[index] = elevatorDetail;
                                }
                            }
                            else
                            {
                                PushData();
                            }
                            Task.Delay(10000).Wait();
                        }
                    }
                    catch (Exception ex)
                    {
                        Task.Delay(10000).Wait();
                    }

                    //Task.Delay(10000).Wait();
                }
            });
        }

        public string GetCmd(int deviceid)
        {
            string cmd = string.Empty;
            byte[] bytes = new byte[6];
            bytes[0] = (byte)deviceid;
            bytes[1] = 0x03;
            bytes[2] = 0x00;
            bytes[3] = 0x00;
            bytes[4] = 0x00;
            bytes[5] = 0x01;
            byte[] crc = Crc18(bytes);
            cmd = ToHexs(bytes) + ToHexs(crc);
            return cmd;
        }

        public string GetSetCmd(int deviceid)
        {
            string cmd = string.Empty;
            byte[] bytes = new byte[6];
            bytes[0] = (byte)ele_detail[0].deviceid;
            bytes[1] = 0x06;
            bytes[2] = 0x00;
            bytes[3] = 0x0F;
            bytes[4] = 0x00;
            bytes[5] = (byte)deviceid;
            byte[] crc = Crc18(bytes);
            cmd = ToHexs(bytes) + ToHexs(crc);
            return cmd;
        }

        public static string ToHexs(byte[] data)
        {
            if (data.Length <= 0) return "";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                sb.Append(data[i].ToString("X2") + " ");
            }
            string str = sb.ToString();
            return str;
        }

        public void Init()
        {
            try
            {
                ele_detail.Clear();
                JsonConfigHelper jsonConfigHelper = new JsonConfigHelper("appsettings.json");
                string ele_name = jsonConfigHelper["ElevatorName"];
                string deviceid = jsonConfigHelper["DeviceID"];
                string Temperature = jsonConfigHelper["Temperature"];
                string[] ele_array = ele_name.Split(',');
                string[] deviceid_array = deviceid.Split(',');
                string[] temp_array = Temperature.Split(',');
                for (int i = 0; i < ele_array.Length; i++)
                {
                    ElevatorDetail elevatorDetail = new ElevatorDetail();
                    elevatorDetail.elevatorname = ele_array[i];
                    elevatorDetail.deviceid = Convert.ToInt32(deviceid_array[i]);
                    elevatorDetail.temperaturethreshold = float.Parse(temp_array[i]);
                    elevatorDetail.cmd = GetCmd(elevatorDetail.deviceid);
                    elevatorDetail.alarm_flag = 0;
                    ele_detail.Add(elevatorDetail);
                }
            }
            catch
            {

            }
        }


        public static bool SendListMsg(string data)
        {
            try
            {
                //LogHelper.Log(data);
                my485SerialPort_1.Write(ToBytesSendDate(data), 0, ToBytesSendDate(data).Length);
                return true;
            }
            catch (Exception e)
            {
                LogHelper.Log(e.ToString());
                return false;
            }
        }

        public bool Wait(int second)
        {
            try
            {
                DateTime startTime = DateTime.Now;
                while (true)
                {
                    TimeSpan secondSpan = new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
                    if (secondSpan.TotalMilliseconds > second)
                    {
                        break;
                    }
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        public static byte[] ToBytesSendDate(string str)
        {
            List<byte> result = new List<byte>();
            if (str.Contains(" "))
            {
                string[] strs = str.Trim().Split(' ');
                foreach (string s in strs)
                {
                    result.Add(Convert.ToByte(s.Trim(), 16));
                }
            }
            else
            {
                result.Add(Convert.ToByte(str, 16));
            }
            return result.ToArray();
        }

        public async void PushData()
        {
            List<DetailPush> detailPushes = QueryDetail();
            await SendMessage(JsonConvert.SerializeObject(detailPushes));
        }

        public static async Task SendMessage(string Message)
        {
            try
            {
                List<WebSocket> web = ElevatorWebSocketMiddleware._sockets;
                if (web != null)
                {
                    foreach (var item in web)
                    {
                        try
                        {
                            if (item.State.ToString() == "Closed")
                            {
                                ElevatorWebSocketMiddleware.RemoveSocket(item);
                                //LogHelper.Log("socket断开");
                            }
                            else
                            {
                                byte[] bMessage = Encoding.UTF8.GetBytes(Message);
                                await item.SendAsync(new ArraySegment<byte>(bMessage), WebSocketMessageType.Text, true, new CancellationToken());
                            }
                        }
                        catch (Exception ex)
                        {
                            continue;
                        }
                    }
                }
            }
            catch
            {

            }
        }
        #endregion

        public List<DetailPush> QueryDetail()
        {
            List<DetailPush> details = new List<DetailPush>();
            foreach (var item in ele_detail)
            {
                DetailPush detailPush = new DetailPush();
                detailPush.elevatorname = item.elevatorname;
                detailPush.deviceid = item.deviceid;
                detailPush.temperature = item.temperature;
                detailPush.temperaturethreshold = item.temperaturethreshold;
                detailPush.alarmTime = item.alarmTime;
                detailPush.updateTime = item.updateTime;
                detailPush.type = item.type;
                detailPush.alarm_temperature = item.alarm_temperature;
                detailPush.alarm_flag = item.alarm_flag;
                details.Add(detailPush);
            }
            return details;
        }
    }
}
